package com.limaopu.myboot.core.serviceimpl;

import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.google.gson.Gson;
import com.google.gson.reflect.TypeToken;
import com.limaopu.myboot.core.common.constant.CommonConstant;
import com.limaopu.myboot.core.common.redis.RedisTemplateHelper;
import com.limaopu.myboot.core.common.vo.PermissionDTO;
import com.limaopu.myboot.core.common.vo.RoleDTO;
import com.limaopu.myboot.core.common.vo.SearchVo;
import com.limaopu.myboot.core.dao.DepartmentDao;
import com.limaopu.myboot.core.dao.UserDao;
import com.limaopu.myboot.core.dao.mapper.PermissionMapper;
import com.limaopu.myboot.core.dao.mapper.UserMapper;
import com.limaopu.myboot.core.dao.mapper.UserRoleMapper;
import com.limaopu.myboot.core.entity.Department;
import com.limaopu.myboot.core.entity.Permission;
import com.limaopu.myboot.core.entity.Role;
import com.limaopu.myboot.core.entity.User;
import com.limaopu.myboot.core.service.UserRoleService;
import com.limaopu.myboot.core.service.UserService;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.StrUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.lang.Nullable;
import org.springframework.security.authentication.AnonymousAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.persistence.criteria.*;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * 用户接口实现
 *
 * @author mac
 */
@Slf4j
@Service
@Transactional
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {

    @Autowired
    private UserDao userDao;

    @Autowired
    private UserRoleMapper userRoleMapper;

    @Autowired
    private UserMapper userMapper;

    @Autowired
    private PermissionMapper permissionMapper;

    @Autowired
    private RedisTemplateHelper redisTemplate;

    @Autowired
    private UserRoleService userRoleService;

    @Autowired
    private DepartmentDao departmentDao;

    @Override
    public UserDao getRepository() {
        return userDao;
    }

    @Override
    public User findByUsername(String username) {

        User user = userDao.findByUsername(username);
        return userToDTO(user);
    }

    @Override
    public User findByMobile(String mobile) {

        User user = userDao.findByMobile(mobile);
        return userToDTO(user);
    }

    @Override
    public User findByEmail(String email) {

        User user = userDao.findByEmail(email);
        return userToDTO(user);
    }

    public User userToDTO(User user) {

        if (user == null) {
            return null;
        }

        // 关联角色
        List<Role> roleList = userRoleMapper.findByUserId(user.getId());
        List<RoleDTO> roleDTOList = roleList.stream().map(e -> {
            return new RoleDTO().setId(e.getId()).setName(e.getName()).setTitle(e.getTitle());
        }).collect(Collectors.toList());

        user.setRoles(roleDTOList);
        log.info("======> roleDTOList " + roleDTOList);
        // 关联权限菜单
        List<Permission> permissionList = permissionMapper.findByUserId(user.getId());
        List<PermissionDTO> permissionDTOList = permissionList.stream()
                // .filter(e -> CommonConstant.PERMISSION_OPERATION.equals(e.getType()))
                // .filter(e -> StrUtil.isNotBlank(e.getApi()))
                .map(e -> {
                    return new PermissionDTO().setTitle(e.getTitle()).setPath(e.getPath()).setApi(e.getApi()).setName(e.getName());
                }).collect(Collectors.toList());

        user.setPermissions(permissionDTOList);
        log.info("======> permissionDTOList " + permissionDTOList);

        return user;
    }

    @Override
    public Page<User> findByCondition(User user, SearchVo searchVo, Pageable pageable) {

        return userDao.findAll(new Specification<User>() {
            @Nullable
            @Override
            public Predicate toPredicate(Root<User> root, CriteriaQuery<?> cq, CriteriaBuilder cb) {

                Path<String> idField = root.get("id");
                Path<String> usernameField = root.get("username");
                Path<String> nicknameField = root.get("nickname");
                Path<String> mobileField = root.get("mobile");
                Path<String> emailField = root.get("email");
                Path<String> departmentIdField = root.get("departmentId");
                Path<String> sexField = root.get("sex");
                Path<Integer> typeField = root.get("type");
                Path<Integer> statusField = root.get("status");
                Path<Date> createTimeField = root.get("createTime");

                List<Predicate> list = new ArrayList<>();

                if (user.getId() > 0) {
                    list.add(cb.equal(idField, user.getId()));
                }

                // 模糊搜素
                if (StrUtil.isNotBlank(user.getUsername())) {
                    list.add(cb.like(usernameField, '%' + user.getUsername() + '%'));
                }
                if (StrUtil.isNotBlank(user.getNickname())) {
                    list.add(cb.like(nicknameField, '%' + user.getNickname() + '%'));
                }
                if (StrUtil.isNotBlank(user.getMobile())) {
                    list.add(cb.like(mobileField, '%' + user.getMobile() + '%'));
                }
                if (StrUtil.isNotBlank(user.getEmail())) {
                    list.add(cb.like(emailField, '%' + user.getEmail() + '%'));
                }

                // 部门
                if (user.getDepartmentId() > 0) {
                    list.add(cb.equal(departmentIdField, user.getDepartmentId()));
                }

                // 性别
                if (StrUtil.isNotBlank(user.getSex())) {
                    list.add(cb.equal(sexField, user.getSex()));
                }
                // 类型
                if (user.getType() != null) {
                    list.add(cb.equal(typeField, user.getType()));
                }
                // 状态
                if (user.getStatus() != null) {
                    list.add(cb.equal(statusField, user.getStatus()));
                }
                // 创建时间
                if (StrUtil.isNotBlank(searchVo.getStartDate()) && StrUtil.isNotBlank(searchVo.getEndDate())) {
                    Date start = DateUtil.parse(searchVo.getStartDate());
                    Date end = DateUtil.parse(searchVo.getEndDate());
                    list.add(cb.between(createTimeField, start, DateUtil.endOfDay(end)));
                }

                // 数据权限
                List<Long> depIds = getDeparmentIds();
                if (depIds != null && depIds.size() > 0) {
                    list.add(departmentIdField.in(depIds));
                }

                Predicate[] arr = new Predicate[list.size()];
                cq.where(list.toArray(arr));
                return null;
            }
        }, pageable);
    }



    /**
     * 获取当前用户数据权限 null代表具有所有权限 包含值为-1的数据代表无任何权限
     */
    @Override
    public List<Long> getDeparmentIds() {

        List<Long> deparmentIds = new ArrayList<>();
        User u = getCurrUser();
        // 读取缓存
        String key = "userRole::depIds:" + u.getId();
        String v = redisTemplate.get(key);
        if (StrUtil.isNotBlank(v)) {
            deparmentIds = new Gson().fromJson(v, new TypeToken<List<Long>>() {
            }.getType());
            return deparmentIds;
        }
        // 当前用户拥有角色
        List<Role> roles = userRoleService.findByUserId(u.getId());
        // 判断有无全部数据的角色
        Boolean flagAll = false;
        for (Role r : roles) {
            if (r.getDataType() == null || r.getDataType().equals(CommonConstant.DATA_TYPE_ALL)) {
                flagAll = true;
                break;
            }
        }
        // 包含全部权限返回null
        if (flagAll) {
            return null;
        }
        // 每个角色判断 求并集
        for (Role r : roles) {
            if (r.getDataType().equals(CommonConstant.DATA_TYPE_UNDER)) {
                // 本部门及以下
                if (u.getDepartmentId() == 0) {
                    // 用户无部门
                    deparmentIds.add(-1L);
                } else {
                    // 递归获取自己与子级
                    List<Long> ids = new ArrayList<>();
                    getRecursion(u.getDepartmentId(), ids);
                    deparmentIds.addAll(ids);
                }
            } else if (r.getDataType().equals(CommonConstant.DATA_TYPE_SAME)) {
                // 本部门
                if (u.getDepartmentId() == 0) {
                    // 用户无部门
                    deparmentIds.add(-1L);
                } else {
                    deparmentIds.add(u.getDepartmentId());
                }
            } else if (r.getDataType().equals(CommonConstant.DATA_TYPE_CUSTOM)) {
                // 自定义
                List<Long> depIds = userRoleService.findDepIdsByUserId(u.getId());
                if (depIds == null || depIds.size() == 0) {
                    deparmentIds.add(-1L);
                } else {
                    deparmentIds.addAll(depIds);
                }
            }
        }
        // 去重
        LinkedHashSet<Long> set = new LinkedHashSet<>(deparmentIds.size());
        set.addAll(deparmentIds);
        deparmentIds.clear();
        deparmentIds.addAll(set);
        // 缓存
        redisTemplate.set(key, new Gson().toJson(deparmentIds), 15L, TimeUnit.DAYS);
        return deparmentIds;
    }

    @Override
    public User getCurrUser() {
        Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
        if (authentication == null || !authentication.isAuthenticated() || authentication.getName() == null
            || authentication instanceof AnonymousAuthenticationToken) {
            return null;
        }
        return findByUsername(authentication.getName());
    }

    private void getRecursion(Long departmentId, List<Long> ids) {

        Department department = departmentDao.getOne(departmentId);
        ids.add(department.getId());
        if (department.getIsParent() != null && department.getIsParent()) {
            // 获取其下级
            List<Department> departments = departmentDao.findByParentIdAndStatusOrderBySortOrder(departmentId, CommonConstant.STATUS_NORMAL);
            departments.forEach(d -> {
                getRecursion(d.getId(), ids);
            });
        }
    }

    @Override
    public List<User> findByDepartmentId(Long departmentId) {

        return userDao.findByDepartmentId(departmentId);
    }

    @Override
    public void updateDepartmentTitle(Long departmentId, String departmentTitle) {

        userDao.updateDepartmentTitle(departmentId, departmentTitle);
    }

    @Override
    public HashMap<Long, User> getByIdsToIdMaps(Collection<Long> ids) {
        List<User> users = getMapper().selectBatchIds(ids);
        HashMap<Long, User> userMaps = new HashMap<Long, User>();

        if (users.size() > 0) {
            users.stream().forEach(e -> {
                userMaps.put(e.getId(), e);
            });
        }

        return userMaps;
    }

    @Override
    public BaseMapper<User> getMapper() {
        return userMapper;
    }
}
